
# *** author zhaomin
# 认证相关的依赖注入和装饰器
# 支持灵活的认证控制
# """

from typing import Optional, Callable, Any
from functools import wraps
from fastapi import Depends, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

from app.core.config import settings
from app.utils.user import verify_token
from app.schemas.base.login import JWTPayload
from app.core.user import CTX_USER_ID
from app.utils.request import RequestHandler


# HTTP Bearer 安全方案
security = HTTPBearer(auto_error=False)


async def get_current_user_optional(
    request: Request,
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
) -> Optional[JWTPayload]:
    """
    可选的用户认证依赖
    如果认证失败或未提供token，返回None而不是抛出异常
    """
    # 如果全局禁用认证，直接返回None
    if not settings.enable_auth:
        return None
    
    # 检查路径是否在白名单中
    if request.url.path in settings.auth_whitelist:
        return None
    
    if not credentials:
        return None
        
    token = credentials.credentials
    user_payload = verify_token(token)
    
    if user_payload:
        # 设置用户上下文
        CTX_USER_ID.set(user_payload.user_id)
        
    return user_payload


async def get_current_user_required(
    request: Request,
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
) -> JWTPayload:
    """
    必需的用户认证依赖
    如果认证失败会抛出HTTP异常
    """
    # 如果全局禁用认证，返回一个默认用户
    if not settings.enable_auth:
        return JWTPayload(
            user_id=0,
            username="anonymous",
            is_superuser=False,
            exp=None
        )
    
    # 检查路径是否在白名单中
    if request.url.path in settings.auth_whitelist:
        return JWTPayload(
            user_id=0,
            username="anonymous", 
            is_superuser=False,
            exp=None
        )
    
    if not credentials:
        raise RequestHandler.unauthorized(
            message = "缺少认证凭证"
        )
        
    token = credentials.credentials
    user_payload = verify_token(token)
    
    if not user_payload:
        raise RequestHandler.unauthorized(
            message ="无效的token或token已过期"
        )
    
    # 设置用户上下文
    CTX_USER_ID.set(user_payload.user_id)
    
    return user_payload


async def get_current_superuser(
    current_user: JWTPayload = Depends(get_current_user_required)
) -> JWTPayload:
    """
    超级用户认证依赖
    
    Args:
        current_user: 当前用户信息
        
    Returns:
        JWTPayload: 超级用户信息
        
    """
    if not current_user.is_superuser:
        raise RequestHandler.forbidden(
            message ="权限不足，需要超级用户权限"
        )
    return current_user


def auth_required(func: Callable) -> Callable:
    """
    认证装饰器，用于强制要求认证的路由
    即使全局禁用认证，使用此装饰器的路由仍需要认证
    
    Args:
        func: 要装饰的函数
        
    Returns:
        Callable: 装饰后的函数
    """
    @wraps(func)
    async def wrapper(*args, **kwargs):
        # 从kwargs中获取request对象
        request = None
        for arg in args:
            if hasattr(arg, 'url') and hasattr(arg, 'method'):
                request = arg
                break
        
        if not request:
            # 如果没有找到request对象，从kwargs中查找
            request = kwargs.get('request')
        
        if not request:
            raise RequestHandler.server_error(
                message ="内部错误：无法获取请求对象"
            )
        
        # 获取Authorization header
        auth_header = request.headers.get("Authorization")
        if not auth_header or not auth_header.startswith("Bearer "):
            raise RequestHandler.unauthorized(
                message ="缺少认证凭证"
            )
        
        token = auth_header.split(" ")[1]
        user_payload = verify_token(token)
        
        if not user_payload:
            raise RequestHandler.unauthorized(
                message ="无效的token或token已过期"
            )
        
        # 设置用户上下文
        CTX_USER_ID.set(user_payload.user_id)
        
        # 将用户信息注入到kwargs中
        kwargs['current_user'] = user_payload
        
        return await func(*args, **kwargs)
    
    return wrapper


def auth_optional(func: Callable) -> Callable:
    """
    可选认证装饰器，用于可选认证的路由
    如果提供了有效token则解析用户信息，否则继续执行
    
    Args:
        func: 要装饰的函数
        
    Returns:
        Callable: 装饰后的函数
    """
    @wraps(func)
    async def wrapper(*args, **kwargs):
        # 从kwargs中获取request对象
        request = None
        for arg in args:
            if hasattr(arg, 'url') and hasattr(arg, 'method'):
                request = arg
                break
        
        if not request:
            request = kwargs.get('request')
        
        current_user = None
        
        if request:
            # 获取Authorization header
            auth_header = request.headers.get("Authorization")
            if auth_header and auth_header.startswith("Bearer "):
                token = auth_header.split(" ")[1]
                user_payload = verify_token(token)
                if user_payload:
                    current_user = user_payload
                    CTX_USER_ID.set(user_payload.user_id)
        
        # 将用户信息注入到kwargs中（可能为None）
        kwargs['current_user'] = current_user
        
        return await func(*args, **kwargs)
    
    return wrapper